
#include "zkcfg.h"
#include "flash.h"
#include "log/log2.h"

#define ZKCFG_VER			0x0001

#define ZKCFG_MANU_ADDR		(FLASH_BASE_ADDR + FLASH_SIZE - FLASH_PAGE_SIZE*1)
#define ZKCFG_KEY_ADDR		(FLASH_BASE_ADDR + FLASH_SIZE - FLASH_PAGE_SIZE*2)
#define ZKCFG_CFG_ADDR		(FLASH_BASE_ADDR + FLASH_SIZE - FLASH_PAGE_SIZE*3)

#define MAGIC_NUM(b0,b1,b2,b3)		(((uint32_t)(b0) << 24) | ((uint32_t)(b1) << 16) | ((uint32_t)(b2) << 8) | (uint32_t)(b3))

#define MAGIC_MANU			MAGIC_NUM('M','A','N','U')
#define MAGIC_KEY			MAGIC_NUM('K','E','Y','0')
#define MAGIC_CFG			MAGIC_NUM('C','F','G','0')


#pragma pack(1)

typedef struct {
	uint32_t magic;
	uint16_t version;
	uint16_t check_sum;
	uint16_t length;
	uint8_t data[];
}zkcfg_store_t;


#pragma pack()

#define ZKCFG_CACHE_BUF_SIZE		256

#define ZKCFG_ST_MAX_SIZE			(ZKCFG_CACHE_BUF_SIZE - sizeof(zkcfg_store_t))

typedef struct {
	uint32_t addr;
	uint32_t buf[ZKCFG_CACHE_BUF_SIZE/sizeof(uint32_t)];
}zkcfg_cache_t;

static zkcfg_cache_t cfg_cache;

static uint16_t zkcfg_store_calc_check_sum(const zkcfg_store_t* st){
	const uint8_t* pdata = st->data;
	const uint8_t* pend = pdata + st->length;
	uint16_t check_sum = 0;
	
	while(pdata < pend){
		check_sum += *pdata;
		pdata++;
	}
	
	return (uint16_t)(~ check_sum);
}

static int zkcfg_store_check(uint32_t magic, const zkcfg_store_t* st){
	if(st->magic != magic){
		return 0;
	}
	const uint16_t check_sum = zkcfg_store_calc_check_sum(st);
	return check_sum == st->check_sum;
}

static void zkcfg_store_write(zkcfg_store_t* st, uint16_t offset, uint16_t length, const void* buf){
	memcpy(st->data + offset, buf, length);
	if(st->length < (offset + length)){
		st->length = offset + length;
	}
}

static void zkcfg_store_build(zkcfg_store_t* st, uint32_t magic){
	st->magic = magic;
	st->check_sum = zkcfg_store_calc_check_sum(st);
}

static inline int zkcfg_addr_check(uint32_t addr){
	switch(addr){
	case ZKCFG_MANU_ADDR:
	case ZKCFG_KEY_ADDR:
	case ZKCFG_CFG_ADDR:
		return 1;
	}
	return 0;
}

static uint32_t zkcfg_addr2magic(uint32_t addr){
	switch(addr){
	case ZKCFG_MANU_ADDR: 	return MAGIC_MANU;
	case ZKCFG_KEY_ADDR:	return MAGIC_KEY;
	case ZKCFG_CFG_ADDR:	return MAGIC_CFG;
	default: return 0;
	}
}

static uint32_t zkcfg_page_to_addr(uint32_t page){
	switch(page){
	case ZKCFG_PAGE_CFG:	return ZKCFG_CFG_ADDR;
	case ZKCFG_PAGE_KEY:	return ZKCFG_KEY_ADDR;
	case ZKCFG_PAGE_MANU:	return ZKCFG_MANU_ADDR;
	default: return 0;
	}
}

void* zkcfg_cache_map(uint32_t addr){
	if(!zkcfg_addr_check(addr)){
		LOG2_ERR("Invalid addr: %08X", addr);
		return NULL;
	}
	flash_read(addr, (uint8_t*)cfg_cache.buf, ZKCFG_CACHE_BUF_SIZE);
	cfg_cache.addr = addr;
	return (void*)cfg_cache.buf;
}

int zkcfg_cache_flush(void){
	uint32_t addr = cfg_cache.addr;
	if(zkcfg_addr_check(addr)){
		flash_erase(addr, 1);
		return flash_write(addr, cfg_cache.buf, ZKCFG_CACHE_BUF_SIZE);
	}
	return -1;
}

int zkcfg_read(uint32_t page, uint16_t offset, uint16_t length, void* data){
	const uint16_t off = offset;
	const uint16_t size = length;
	const uint32_t addr = zkcfg_page_to_addr(page);
	
	if((size + off) > ZKCFG_ST_MAX_SIZE){
		LOG2_ERR("out of range: %d!", (size + off));
		return -1;
	}

	zkcfg_store_t* st = zkcfg_cache_map(addr);
	if(!st){
		return -1;
	}
	const uint32_t magic = zkcfg_addr2magic(addr);
	if(!zkcfg_store_check(magic, st)){
		LOG2_ERR("invalid store");
		return -2;
	}
	memcpy(data, &st->data[off], size);
	return 0;
}

static inline int zkcfg_check_update(zkcfg_store_t* st, uint16_t offset, uint16_t length, const void* data){
	if(st->length < (offset + length)){
		return 1;
	}
	return memcmp(st->data + offset, data, length);
}

int zkcfg_write(uint32_t page, uint16_t offset, uint16_t length, const void* data){
	const uint16_t off = offset;
	const uint16_t size = length;
	const uint32_t addr = zkcfg_page_to_addr(page);
	
	if((size + off) > ZKCFG_ST_MAX_SIZE){
		LOG2_ERR("out of range: %d!", (size + off));
		return -1;
	}

	zkcfg_store_t* st = zkcfg_cache_map(addr);
	if(!st){
		return -1;
	}
	const uint32_t magic = zkcfg_addr2magic(addr);
	if(!zkcfg_store_check(magic, st)){
		LOG2_WRN("invalid store");
		memset(st, 0, ZKCFG_CACHE_BUF_SIZE);
	}
	if(!zkcfg_check_update(st, offset, length, data)){
		return 0;
	}
	zkcfg_store_write(st, off, size, data);
	zkcfg_store_build(st, magic);
	return zkcfg_cache_flush();
}

int zkcfg_read_keys(zkcfg_keys_t* keys){
	const uint16_t off = 0;
	const uint16_t size = sizeof(*keys);
	return zkcfg_read(ZKCFG_PAGE_KEY, off, size, keys);
}

int zkcfg_write_keys(const zkcfg_keys_t* keys){
	const uint16_t off = 0;
	const uint16_t size = sizeof(*keys);
	return zkcfg_write(ZKCFG_PAGE_KEY, off, size, keys);
}


void zkcfg_get_default_keys(zkcfg_keys_t* keys){
	static const zkcfg_key_data_t default_keys[2] = {
		{
			.kid = 0,
			.type  = ZKCFG_KT_DES,
			.length = 16,
			.key = {0x30, 0x84, 0x5e, 0xf6, 0x5b, 0x17, 0x25, 0x20, 
					0xb7, 0x57, 0x36, 0x01, 0x2c, 0x24, 0xd1, 0x16},
		},
		{
			.kid = 1,
			.type  = ZKCFG_KT_AES,
			.length = 16,
			.key = {0xab, 0x43, 0x7c, 0xb9, 0xf5, 0x9c, 0xb8, 0x19, 
					0xf6, 0x70, 0x9d, 0xc1, 0x8f, 0xea, 0xf0, 0xb4},
		},
	};
#if 0
	static const zkcfg_key_data_t default_key = {
		.kid = 2,
		.type  = ZKCFG_KT_AES,
		.length = 16,
		.keys = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
				0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
	};
#endif 
		
	keys->privacy = default_keys[0];
	keys->credential = default_keys[1];
}

void zkcfg_get_default_cfg(zkcfg_cfg_t * cfg){
	static const zkcfg_keyset_t default_keyset[2] = {
		{
			.aid = 0xA11E00,
			.key_id = 0,
			.app = 	ZKCFG_APP_PRIV,
			.key_type  = ZKCFG_KT_DES,
			.key_num = 0,
			.diversified = 0,
		},
		{
			.aid = 0xA11E01,
			.key_id = 1,
			.app = 	ZKCFG_APP_CRED,
			.key_type  = ZKCFG_KT_AES,
			.key_num = 0,
			.diversified = 1,
		},
	};
	
	cfg->privacy = default_keyset[0];	
	cfg->credential = default_keyset[1];
	cfg->file_no = 1;
	cfg->offset = 0;
	cfg->length = 16;
}

static void zkcfg_key_data_dbg(const zkcfg_key_data_t* kdata){
	LOG2_PRINT("\r\n key data: \n");
	LOG2_PRINT("\tkid: %d\n", kdata->kid);
	LOG2_PRINT("\ttype: %d\n", kdata->type);
	LOG2_PRINT("\tlength: %d\n", kdata->length);
	LOG2_PRINT("\tkeys:");
	LOG2_DUMP_HEX(kdata->key, kdata->length);
	LOG2_PRINT("\r\n");
}

void zkcfg_key_data_debug(const zkcfg_key_data_t* kdata, uint16_t count){
	for(int i = 0; i < count; i++){
		zkcfg_key_data_dbg(&kdata[i]);
	}
}

static void zkcfg_keyset_dbg(const zkcfg_keyset_t* ks){
	LOG2_PRINT("\r\n key set: \n");
	LOG2_PRINT("\taid: %06X\n", ks->aid);
	LOG2_PRINT("\tapp: %d\n", ks->app);
	LOG2_PRINT("\tkey_id: %d\n", ks->key_id);
	LOG2_PRINT("\tkey_type %d\n", ks->key_type);
	LOG2_PRINT("\tkey_num: %d\n", ks->key_num);
	LOG2_PRINT("\tdiversified: %d\n", ks->diversified);
	LOG2_PRINT("\r\n");
}

void zkcfg_keyset_debug(const zkcfg_keyset_t *keyset, uint16_t count){
	for(int i = 0; i < count; i++){
		zkcfg_keyset_dbg(keyset+i);
	}
}


